#include "czDisplay.h"
#include "ContextEGL2.h"

#include "InputListenerDefault.h"


#include <math.h>
#include <GLES2/gl2.h>

#include <glShaderUtil.h>
#include "czUtil.h"

#ifdef _WIN32
#pragma comment (lib, "libGLESv2.lib")
#pragma comment (lib, "libEGL.lib")
#endif

#define VERTEX_ARRAY 0

#define MEDIA_DIR "../media/"

float pfIdentity[] =
{
  1.0f,0.0f,0.0f,0.0f,
  0.0f,1.0f,0.0f,0.0f,
  0.0f,0.0f,1.0f,0.0f,
  0.0f,0.0f,0.0f,1.0f
};

ContextEGL2 ctx;
MyListener li;
WindowClass window;

int main(int argc, char** argv)
{
  ctx.setFlags();
  if( !window.open(&ctx,&li, "title", 640, 480) )
    return -1;

  window.center();
  window.show();


  GLuint uiFragShader, uiVertShader;		/* Used to hold the fragment and vertex shader handles */
  GLuint uiProgramObject;					/* Used to hold the program handle (made out of the two previous shaders */


  // Create the fragment shader object
  uiFragShader = glCreateShader(GL_FRAGMENT_SHADER);

  unsigned sz=0;
  char* buf = file2mem(MEDIA_DIR"flagmentShader.txt", &sz);
  // Load the source code into it
  glShaderSource(uiFragShader, 1, (const char**)&buf, NULL);
  freemem(buf);

  // Compile the source code
  glCompileShader(uiFragShader);
  if( !ShaderCompiledTest(uiFragShader) )
    return -1;

  // Loads the vertex shader in the same way
  uiVertShader = glCreateShader(GL_VERTEX_SHADER);
  buf = file2mem(MEDIA_DIR"vertexShader.txt", &sz);
  glShaderSource(uiVertShader, 1, (const char**)&buf, NULL);
  freemem(buf);

  glCompileShader(uiVertShader);
  if( !ShaderCompiledTest(uiVertShader) )
    return -1;

  // Create the shader program
  uiProgramObject = glCreateProgram();

  // Attach the fragment and vertex shaders to it
  glAttachShader(uiProgramObject, uiFragShader);
  glAttachShader(uiProgramObject, uiVertShader);

  // Bind the custom vertex attribute "myVertex" to location VERTEX_ARRAY
  glBindAttribLocation(uiProgramObject, VERTEX_ARRAY, "myVertex");

  // Link the program
  glLinkProgram(uiProgramObject);
  if( !ProgramLinkedTest(uiProgramObject) )
    return -1;

  // Actually use the created program
  glUseProgram(uiProgramObject);

  // Vertex position (x,y,z)
  GLfloat pfVertices[] = {-0.4f,-0.4f,0.0f, +0.4f,-0.4f,0.0f, 0.0f,0.4f,0.0f};

  /*
  Enable the custom vertex attribute at index VERTEX_ARRAY.
  We previously binded that index to the variable in our shader "vec4 MyVertex;"
  */
  glEnableVertexAttribArray(VERTEX_ARRAY);

  // Sets the vertex data to this attribute index
  glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, pfVertices);

  // Sets the clear color.
  // The colours are passed per channel (red,green,blue,alpha) as float values from 0.0 to 1.0
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

  GLint numUniforms;
  GLint maxUniformLength;
  char* uniformName;

  // Draws a triangle for 800 frames
  while(window.isRun())
  {
    glClear(GL_COLOR_BUFFER_BIT);

    /*
    Bind the projection model view matrix (PMVMatrix) to
    the associated uniform variable in the shader
    */

#if 0
    /// query uniforms
    glGetProgramiv(uiProgramObject, GL_ACTIVE_UNIFORMS, &numUniforms);
    glGetProgramiv(uiProgramObject, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLength);
    uniformName = (char*) malloc(maxUniformLength);

    for(int idx=0; idx<numUniforms; idx++) {
      GLint elemNum, location;
      GLenum type;
      glGetActiveUniform(uiProgramObject, idx, maxUniformLength, NULL, &elemNum, &type, uniformName);
      switch(type) {
        case GL_FLOAT:
          break;
        case GL_FLOAT_VEC2:
          break;
        case GL_FLOAT_VEC3:
          break;
        case GL_FLOAT_VEC4:
          break;
        case GL_FLOAT_MAT4:
          break;
        case GL_FLOAT_MAT3:
          break;
        case GL_FLOAT_MAT2:
          break;
        //case GL_FLOAT_MAT3x4:
        //  break;
        //case GL_FLOAT_MAT4x3:
        //  break;
        default:
          break;
      }
    }
    free(uniformName);
#endif

    // First gets the location of that variable in the shader using its name
    int i32Location = glGetUniformLocation(uiProgramObject, "myPMVMatrix");

    // Then passes the matrix to that variable
    glUniformMatrix4fv( i32Location, 1, GL_FALSE, pfIdentity);

    /// query for export binary format of shaders
    //GLboolean existShaderCompiler;
    //GLint numBinFormats;
    //GLint* formats;
    //GLint shaderNum;
    //glGetBooleanv(GL_SHADER_COMPILER, &existShaderCompiler);
    //glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &numBinFormats);
    //GLint shaderBinFormats;
    //glGetIntegerv(GL_SHADER_BINARY_FORMATS, &shaderBinFormats);
    //GLint binFmt;
    //glGetIntegerv(GL_PLATFORM_BINARY, &binFmt);

    //formats = (GLint*)malloc(sizeof(GLint)*numBinFormats);
    //glGetIntegerv(GL_SHADER_BINARY_FORMATS, formats);
    //glGetProgramiv(uiProgramObject, GL_ATTACHED_SHADERS, &shaderNum);
    ////GLuint* shaders = new GLuint[shaderNum];
    ////glShaderBinary(shaderNum, &shaders, formats, binary, binLength);  // BUG!!! not implemt
    ////delete [] shaders;
    //free(formats);

    /*
    Draws a non-indexed triangle array from the pointers previously given.
    This function allows the use of other primitive types : triangle strips, lines, ...
    For indexed geometry, use the function glDrawElements() with an index list.
    */
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glFlush();
    window.update();
  }

  // Frees the OpenGL handles for the program and the 2 shaders
  glDeleteProgram(uiProgramObject);
  glDeleteShader(uiFragShader);
  glDeleteShader(uiVertShader);

  window.close();

  return 0;
}
